#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

int get_width(int fd)
{
    //保证光标在开始位置
    lseek(fd, 0, SEEK_SET);
    
    //获取BMP图片中头文件信息
    char buf[54];
    memset(buf, 0, 54);

    //获取图片长与宽
    int ret = read(fd, buf, 54);
    if(ret < 0)
    {
        perror("read image fail");
        exit(1);
    }
    int width = (buf[21]<<24) | (buf[20]<<16) | (buf[19]<<8) | (buf[18]);

    return width;
}

int get_high(int fd)
{
    //保证光标在开始位置
    lseek(fd, 0, SEEK_SET);

    //获取BMP图片中头文件信息
    char buf[54];
    memset(buf, 0, 54);
    
    //获取图片长
    int ret = read(fd, buf, 54);
    if(ret < 0)
    {
        perror("read image fail");
        exit(1);
    }
    int high = (buf[25]<<24) | (buf[24]<<16) | (buf[23]<<8) | (buf[22]);

    return high;
}

void bit_move(int fd_dev, int fd, int width, int high)
{
    
    //任意照片的大小的长与宽都必须是4的倍数
    //BMP图片中前54个字节是图片信息，因此需要跳过
    //BMP图片的像素点为BGR 
    //LCD屏幕的像素点为ARGB
    //需要换位置

    //内存映射
    int *addr = (int *)mmap(NULL, width*high*4, PROT_READ | PROT_WRITE, MAP_SHARED, fd_dev, 0);
    if(addr == MAP_FAILED)
    {
        perror("mmap fd_lcd fail");
        exit(1);
    }

    //图片的像素点保存数组
    char *buf_ch = (char *)malloc(width*high*3);
    memset(buf_ch, 0, width*high*3);

    //将光标移到实际有效的图片字节
    lseek(fd, 54, SEEK_SET);

    //将像素点保存到数组buf_ch里面
    int ret = read(fd, buf_ch, width*high*3);
    if(ret < 0)
    {
        perror("read image fail");
        exit(1);
    }

    //行
    int x = 0;
    //列
    int y = 0;
    for(y=0;y<high;y++)
    {
        
        for(x=0;x<width;x++) 
            *(addr+(high-1-y)*width+x) = buf_ch[(y*width+x)*3+0] |
                                buf_ch[(y*width+x)*3+1]<<8 |
                                buf_ch[(y*width+x)*3+2]<<16;
    }

    free(buf_ch);
    munmap(addr, width*high*4);
}

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        printf("./lcd xxx.bmp");
        exit(1);
    }

    int fd_lcd = open("/dev/fb0", O_RDWR);
    if(fd_lcd < 0)
    {
        perror("open /dev/fb0 fail");
        exit(1);
    }

    int fd = open(argv[1], O_RDWR);
    if(fd < 0)
    {
        perror("open image fail");
        exit(1);
    }
     
    bit_move(fd_lcd, fd, get_width(fd), get_high(fd));
   
    close(fd);
    close(fd_lcd);
    return 0;
}
